//FractionNumber.java
//Timothy Grant
//CS1410
//Calculator 1410
//Final Version

/*
 * Ratio indicated with colon instead of slash.
 * No method to reduce fractions.
 */
package numbers;

import java.util.Arrays;
import java.util.List;

public class FractionNumber implements Number {
    //instance fields

    private int numer;
    private int denom;
    private static String[] validOps = {"+", "-", "*", "/", "%"};
    private static String[] hiOps = {"*", "/", "%"};

    //begin set constructor
    public FractionNumber(int n, int d) {
        numer = n;

        //check for zero in the denominator
        if (d == 0) {
            throw new ArithmeticException("Denominator cannot be zero.");
        } else {
            denom = d;
        }
    }

    public FractionNumber(String ns, String ds) {
        numer = Integer.parseInt(ns);

        if (ds.equals("0")) {
            throw new ArithmeticException("Denominator cannot be zero.");
        } else {
            denom = Integer.parseInt(ds);
        }
    }

    //add()
    public FractionNumber add(FractionNumber rightOpand) {
        return new FractionNumber((this.numer * rightOpand.denom) + (this.denom * rightOpand.numer), this.denom * rightOpand.denom);
    }//end add method

    //subract()
    public FractionNumber subtract(FractionNumber rightOpand) {
        return new FractionNumber((this.numer * rightOpand.denom) - (this.denom * rightOpand.numer), this.denom * rightOpand.denom);
    }//end subtract method

    //multiply()
    public FractionNumber multiply(FractionNumber rightOpand) {
        return new FractionNumber(this.numer * rightOpand.numer, this.denom * rightOpand.denom);
    }//end multiply method

    //divide()
    public FractionNumber divide(FractionNumber rightOpand) {
        if (rightOpand.numer == 0) {
            throw new ArithmeticException("Cannot divide by zero.");
        }
        return new FractionNumber(this.numer * rightOpand.denom, this.denom * rightOpand.numer);
    }//end divide method

    //equals()
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof FractionNumber) {
            FractionNumber temp = (FractionNumber) obj;
            return (this.numer * temp.denom) == (this.denom * temp.numer);
        } else {
            return false;
        }
    }//end equals method

    //toString()
    @Override
    public String toString() {
        return numer + ":" + denom;
    }//end toString method

    //begin isValidOperator()
    public boolean isValidOperator(String s) {
        for (String x : validOps) {
            if (s.equals(x)) {
                return true;
            }
        }
        return false;
    }//end isValidOperator()

    //begin opand2Str()
    public String opand2Str(String e, int i) {
        int strIndex = i;
        char nextCh;
        String retStr = "";

        try {
            retStr += new IntegerNumber(0).opand2Str(e, i);
            strIndex += retStr.length();
        } catch (IndexOutOfBoundsException ex) {
            throw new ArithmeticException("Invalid fraction operand");
        }

        try {
            nextCh = e.charAt(strIndex);
            ++strIndex;
        } catch (IndexOutOfBoundsException ex) {
            return retStr;
        }

        if (Character.isWhitespace(nextCh)) {
            throw new ArithmeticException("Invalid fraction operand");
        }

        if (nextCh != ':') {
            return retStr;
        }

        retStr += nextCh;

        try {
            retStr += new IntegerNumber(0).opand2Str(e, strIndex);
            strIndex += retStr.length();
        } catch (IndexOutOfBoundsException ex) {
            throw new ArithmeticException("Invalid fraction operand");
        }
        return retStr;
    }//end opand2Str()

    //begin op2Str()
    public String op2Str(String e, int i) {
        int strIndex = i;
        char nextCh;
        String retStr = ("");

        try {
            nextCh = e.charAt(strIndex);
        } catch (IndexOutOfBoundsException ex) {
            throw new ArithmeticException("Invalid operation");
        }

        if (isValidOperator(Character.toString(nextCh))) {
            retStr += nextCh;
            return retStr;
        } else if (!isValidOperator(Character.toString(nextCh))) {
            throw new ArithmeticException("Invalid operation");
        }

        return retStr;
    }//end op2Str()

    //begin str2Number()
    public Number str2Number(String s) {
        String numerStr = "";
        String denomStr = "";
        char nextCh;
        int strIndex = 0;

        try {
            nextCh = s.charAt(strIndex);
        } catch (IndexOutOfBoundsException ex) {
            throw new ArithmeticException(s + " is an invalid fraction number");
        }

        if (nextCh == '+') {
            try {
                numerStr += new IntegerNumber(0).opand2Str(s, ++strIndex);
                strIndex += numerStr.length();
            } catch (ArithmeticException ex) {
                throw new ArithmeticException("invalid fraction");
            }
        } else {
            try {
                numerStr += new IntegerNumber(0).opand2Str(s, strIndex);
                strIndex += numerStr.length();
            } catch (ArithmeticException ex) {
                throw new ArithmeticException(s + " is an invalid fraction number");
            }
        }

        if (strIndex == s.length()) {
            return new FractionNumber(numerStr, "1");
        }

        try {
            nextCh = s.charAt(strIndex);
        } catch (IndexOutOfBoundsException ex) {
            throw new ArithmeticException(s + " is an invalid fraction number");
        }

        if (Character.isWhitespace(nextCh)) {
            throw new ArithmeticException(s + " is an invalid fraction number");
        }

        if (nextCh != ':') {
            return new FractionNumber(numerStr, "1");
        }

        try {
            nextCh = s.charAt(++strIndex);
        } catch (IndexOutOfBoundsException ex) {
            throw new ArithmeticException(s + " is an invalid fraction number");
        }

        if (Character.isWhitespace(nextCh)) {
            throw new ArithmeticException(s + " is an invalid fraction number");
        }

        if (nextCh == '+') {
            try {
                denomStr += new IntegerNumber(0).opand2Str(s, ++strIndex);
                strIndex += denomStr.length();
            } catch (ArithmeticException ex) {
                throw new ArithmeticException("invalid fraction");
            }
        } else {
            try {
                denomStr += new IntegerNumber(0).opand2Str(s, strIndex);
                strIndex += denomStr.length();
            } catch (ArithmeticException ex) {
                throw new ArithmeticException(s + " is an invalid fraction number");
            }
        }
        return new FractionNumber(numerStr, denomStr);
    }//end str2Number()

    //begin doOp()
    public Number doOp(Number n1, Number n2, String op) {
        FractionNumber result = new FractionNumber(0, 1);
        FractionNumber num1;
        FractionNumber num2;
        //System.out.printf("n1= %s, n2= %s%n", n1.toString(), n2.toString());
        try {
            num1 = (FractionNumber) n1;
            num2 = (FractionNumber) n2;
            //result = null;
        } catch (ClassCastException ex) {
            throw new ArithmeticException("Invalid Operation");
        }
        if (op.equals("+")) {
            try {
                result = num1.add(num2);
            } catch (ArithmeticException ex) {
                throw new ArithmeticException("Invalid Operation");
            }
        } else if (op.equals("-")) {
            try {
                result = num1.subtract(num2);
            } catch (ArithmeticException ex) {
                throw new ArithmeticException("Invalid Operation");
            }
        } else if (op.equals("*")) {
            try {
                result = num1.multiply(num2);
            } catch (ArithmeticException ex) {
                throw new ArithmeticException("Invalid Operation");
            }
        } else if (op.equals("/")) {
            try {
                result = num1.divide(num2);
            } catch (ArithmeticException ex) {
                throw new ArithmeticException("Invalid Operation");
            }
        } else {
            throw new ArithmeticException("Invalid Operation");
        }
        return result;
    }//end doOp()

    //begin isLower()
    public boolean isLower(String op1, String op2) {
        List<String> hiOpsList = Arrays.asList(hiOps);
        if (hiOpsList.contains(op1) && hiOpsList.contains(op2)) {
            return false;
        } else if (!hiOpsList.contains(op1) && !hiOpsList.contains(op2)) {
            return false;
        } else if (hiOpsList.contains(op1) && !hiOpsList.contains(op2)) {
            return false;
        }
        return true;
    }//end isLower()
}//end Class FractionNumber

